﻿#include "AIUITest.h"
#include "json/json.h"
using namespace VA;
//for ivw
/*
#include "msp_cmn.h"

#ifdef _WIN64
#pragma comment(lib,"msc_x64.lib") //x64
#else
#pragma comment(lib,"msc.lib") //x86
#endif
*/

static const string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/* 循环写入测试音频，每次写1280B */
bool WriteAudioThread::threadLoop()
{
	char audio[1280];
	int len = mFileHelper->read(audio, 1280);

	if (len > 0)
	{
		Buffer* buffer = Buffer::alloc(len);
		memcpy(buffer->data(), audio, len);

		const wchar_t * para = L"data_type=audio,sample_rate=16000,msc.lng=117.14430236816406,msc.lat=31.831058502197266,"
					L"rec_user_data={\"sceneInfo\":{},\"recHotWords\":\"返回|主界面|通知栏|图片|百度|本地|主页|主目录|根目录|下载|库|退出|主题|设置|网络硬盘|新建|搜索|刷新|视图|窗口\"}";
		unsigned long len = ::WideCharToMultiByte (CP_UTF8, NULL, para, -1, NULL, NULL,NULL, NULL);
		char *mbcbuffer = new char[len];
		::WideCharToMultiByte (CP_UTF8, NULL, para, -1, mbcbuffer, len, NULL, NULL);		
		IAIUIMessage * writeMsg = IAIUIMessage::create(AIUIConstant::CMD_WRITE,
			0, 0,  (const char*)mbcbuffer, buffer);
		delete[] mbcbuffer;

		if (NULL != mAgent)
		{
			mAgent->sendMessage(writeMsg);
		}		
#ifdef _MSC_VER
		Sleep(40);
#else
		usleep(40 * 1000);
#endif
		writeMsg->destroy();
	} else {
		if (mRepeat)
		{
			mFileHelper->rewindReadFile();
		} else {
			IAIUIMessage * stopWrite = IAIUIMessage::create(AIUIConstant::CMD_STOP_WRITE,
				0, 0, "data_type=audio,sample_rate=16000");

			if (NULL != mAgent)
			{
				mAgent->sendMessage(stopWrite);
			}
			stopWrite->destroy();

			mFileHelper->closeReadFile();
			mRun = false;
		}
	}

	return mRun;
}


unsigned int __stdcall WriteAudioThread::WriteProc(void * paramptr)
{
	WriteAudioThread * self = (WriteAudioThread *)paramptr;

	while (1) {
		if (! self->threadLoop())
			break;
	}
		
	return 0;
}

WriteAudioThread::WriteAudioThread(IAIUIAgent* agent, const string& audioPath, bool repeat):
mAgent(agent), mAudioPath(audioPath), mRepeat(repeat), mRun(true), mFileHelper(NULL)
	,thread_hdl_(NULL)
{
	mFileHelper = new FileUtil::DataFileHelper("");
	mFileHelper->openReadFile(mAudioPath, false);
}

WriteAudioThread::~WriteAudioThread()
{
	if (NULL != mFileHelper)
	{
		delete mFileHelper;
		mFileHelper = NULL;
	}
}

void WriteAudioThread::stopRun()
{
	mRun = false;
	WaitForSingleObject(thread_hdl_, INFINITE);
	thread_hdl_ = NULL;
}

bool WriteAudioThread::run()
{
	if (thread_hdl_ == NULL) {
		thread_hdl_ = (HANDLE)_beginthreadex(NULL, 0, WriteProc, (LPVOID) this, 0,  &thread_id_);
		return true;
	}

	return false;
}

AIUITester::AIUITester() : agent(NULL), writeThread(NULL)
{
	//AIUISetting::setSaveDataLog(true);
	AIUISetting::setAIUIDir(TEST_ROOT_DIR);
	AIUISetting::initLogger(LOG_DIR);
}

AIUITester::~AIUITester() 
{
	if (agent) {
		agent->destroy();
		agent = NULL;
	}
}
/*
	创建AIUI代理，传入用户设置的参数，通过生成的AIUI代理对象agent给AIUI SDK发命令
*/
void AIUITester::createAgent(IAIUIListener * m_listener)
{
	string appid = "5c63fe00";

	Json::Value paramJson;
	Json::Value appidJson;

	appidJson["appid"] = appid;

	string fileParam = FileUtil::readFileAsString(CFG_FILE_PATH);

	Json::Reader reader;
	if(reader.parse(fileParam, paramJson, false))
	{
		paramJson["login"] = appidJson;

		
		/* 如果配置了使用单路ivw唤醒，那么需要用户自己将支持单路唤醒的msc.dll放在aiui.dll同目录下，并且
			配置好唤醒资源的路径；
			如果设置了单路唤醒，在createAgent()成功之后，就不需要调用wakeUp()接口通过外部发送命令的方式进行唤醒，可以通过写入唤醒音频的方式；
			默认没有设置ivw唤醒。
		*/
		string wakeup_mode = paramJson["speech"]["wakeup_mode"].asString();
		if(wakeup_mode == "ivw")
		{
			/*
				使用单路唤醒时，首先需要调动msc库里面的MSPLogin()
			*/
			//string lgiparams = "appid=5c63fe00,engine_start=ivw";
			//MSPLogin(NULL, NULL, lgiparams.c_str());
			string ivw_res_path = paramJson["ivw"]["res_path"].asString();
			if(!ivw_res_path.empty())
			{
				ivw_res_path = "fo|" + ivw_res_path;
				paramJson["ivw"]["res_path"] = ivw_res_path;
			}

			string ivw_lib_path;
			#ifdef _WIN64
				ivw_lib_path = "msc_x64.dll";
			#else
				ivw_lib_path = "msc.dll";
			#endif

			paramJson["ivw"]["msc_lib_path"] = ivw_lib_path;
		}

		Json::FastWriter writer;
		string paramStr = writer.write(paramJson);
		agent = IAIUIAgent::createAgent(paramStr.c_str(), m_listener);
	}
	else
	{
		cout << "aiui.cfg has something wrong!" << endl;
	}
}

/*
	外部唤醒接口，通过发送CMD_WAKEUP命令使AIUI SDK转入唤醒状态，
	唤醒状态下就可以写入文本或语义，得到结果
*/
void AIUITester::wakeup()
{
	if (NULL != agent)
	{
		IAIUIMessage * wakeupMsg = IAIUIMessage::create(AIUIConstant::CMD_WAKEUP);
		agent->sendMessage(wakeupMsg);
#ifdef BUG_TEST
		delete wakeupMsg;///  BUG TEST.
#else
		wakeupMsg->destroy();
#endif
	}
}

/*启动AIUI SDK, 该接口与stop()接口对应，只有在调用了stop()接口停止了AIUI SDK的情况下，
需要再次启动AIUI SDK时调用该接口
*/
void AIUITester::start()
{
	if (NULL != agent)
	{
		IAIUIMessage * startMsg = IAIUIMessage::create (AIUIConstant::CMD_START);
		agent->sendMessage(startMsg);
		startMsg->destroy();
	}
}


/*停止AIUI      SDK，停止之后处于空闲状态  */
void AIUITester::stop()
{
	if (NULL != agent)
	{
		IAIUIMessage *stopMsg = IAIUIMessage::create (AIUIConstant::CMD_STOP);
		agent->sendMessage(stopMsg);
		stopMsg->destroy();
	}
}


/*
	写入音频接口
	开启新的线程写入音频，音频从文件读取
*/
void AIUITester::write(bool repeat)
{
	if (agent == NULL)
		return;

	if (writeThread != NULL) {
		writeThread->stopRun();
		delete writeThread;
		writeThread = NULL;
	}
	writeThread = new WriteAudioThread(agent, TEST_AUDIO_PATH,  repeat);
	writeThread->run();
}


/*主动销毁写入音频的线程*/
void AIUITester::stopWriteThread()
{
	if (writeThread) {
		writeThread->stopRun();
		delete writeThread;
		writeThread = NULL;
	}
}

/*
	状态重置接口，发送CMD_RESET命令给AIUI SDK之后，SDK原本的唤醒状态会被重置为非唤醒状态，
	需要重新唤醒才能再次写入音频或文本
*/
void AIUITester::reset()
{
	if (NULL != agent)
	{
		IAIUIMessage * resetMsg = IAIUIMessage::create(AIUIConstant::CMD_RESET);
		agent->sendMessage(resetMsg);
		resetMsg->destroy();
	}
}

/*
	文本语义接口
*/
void AIUITester::writeText(string msg)
{
	if (NULL != agent)
	{
		//const wchar_t * text = L"天气";

		/*传入的文本需要转化为utf-8编码*/
		//unsigned long len = ::WideCharToMultiByte (CP_UTF8, NULL, text, -1, NULL, NULL,NULL, NULL) - 1;
		//char *mbcbuffer = new char[len];
		//::WideCharToMultiByte (CP_UTF8, NULL, text, -1, mbcbuffer, len, NULL, NULL);

		unsigned long len = msg.length();

		Buffer* buffer = Buffer::alloc(len);
		memcpy(buffer->data(), msg.c_str(), len);

		IAIUIMessage * writeMsg = IAIUIMessage::create(AIUIConstant::CMD_WRITE,
			0,0, "data_type=text", buffer);	

		//delete[] mbcbuffer;		
		agent->sendMessage(writeMsg);
		writeMsg->destroy();		
	}
}

/* 
	查询数据同步状态
*/
void AIUITester::querySyncStatus(string mSyncSid)
{
	if (NULL != agent)
	{
		Json::Value paramJson;

		/* 参数包含需要对应同步上传操作的sid */
		cout << "query sid is " << mSyncSid << endl;
		paramJson["sid"] = mSyncSid;

		Json::FastWriter writer;
		string paramStr = writer.write(paramJson);

		IAIUIMessage * querySyncMsg = IAIUIMessage::create(AIUIConstant::CMD_QUERY_SYNC_STATUS,
			AIUIConstant::SYNC_DATA_SCHEMA, 0, paramStr.c_str(), NULL);

		agent->sendMessage(querySyncMsg);

		querySyncMsg->destroy();
	}
}

string AIUITester::encode(const unsigned char* bytes_to_encode, unsigned int in_len)
{
	std::string ret;
	int i = 0;
	int j = 0;
	unsigned char char_array_3[3];
	unsigned char char_array_4[4];

	while (in_len--) {
		char_array_3[i++] = *(bytes_to_encode++);
		if (i == 3) {
			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;

			for(i = 0; (i <4) ; i++)
				ret += base64_chars[char_array_4[i]];
			i = 0;
		}
	}

	if (i)
	{
		for(j = i; j < 3; j++)
			char_array_3[j] = '\0';

		char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
		char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
		char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
		char_array_4[3] = char_array_3[2] & 0x3f;

		for (j = 0; (j < i + 1); j++)
			ret += base64_chars[char_array_4[j]];

		while((i++ < 3))
			ret += '=';

	}

	return ret;
}

void AIUITester::syncSchema()
{
	if (NULL != agent)
	{
		Json::Value paramsJson;
		paramsJson["tag"] = "tag_abc";

		Json::Value dataParamJson;
		dataParamJson["id_name"] = "uid";
		dataParamJson["res_name"] = "IFLYTEK.telephone_contact";

		const wchar_t * text = L"{\"name\":\"程序员\", \"alias\":\"开发\"}\n{\"name\":\"设计师\", \"alias\":\"设计\"}\n{\"name\":\"产品经理\", \"alias\":\"产品\"}";
		unsigned long len = ::WideCharToMultiByte(CP_UTF8, NULL, text, -1, NULL, NULL,NULL, NULL) - 1;
		char *mbcbuffer = new char[len];
		::WideCharToMultiByte (CP_UTF8, NULL, text, -1, mbcbuffer, len, NULL, NULL);

		string rawDataBase64 = encode((const unsigned char*) mbcbuffer, (unsigned int) len);

		delete[] mbcbuffer;

		Json::Value dataJson;
		dataJson["param"] = dataParamJson;
		dataJson["data"] = rawDataBase64;

		Json::FastWriter writer;

		string dataStr = writer.write(dataJson);
		string params = writer.write(paramsJson);

		Buffer* dataBuffer = Buffer::alloc(dataStr.length());
		dataStr.copy((char*) dataBuffer->data(), dataStr.length());

		IAIUIMessage * syncMsg = IAIUIMessage::create(AIUIConstant::CMD_SYNC,
			AIUIConstant::SYNC_DATA_SCHEMA, 0, params.c_str(), dataBuffer);

		agent->sendMessage(syncMsg);

		syncMsg->destroy();
	}
}


void AIUITester::startTts()
{
	if (NULL != agent)
	{
		const wchar_t * text = L"科大讯飞"; 

		/*传入的文本需要转化为utf-8编码*/
		unsigned long len = ::WideCharToMultiByte (CP_UTF8, NULL, text, -1, NULL, NULL,NULL, NULL) - 1;
		char *mbcbuffer = new char[len];
		::WideCharToMultiByte (CP_UTF8, NULL, text, -1, mbcbuffer, len, NULL, NULL);

		Buffer* textData = Buffer::alloc(len);
		memcpy(textData->data(), mbcbuffer, len);

		if (mbcbuffer != NULL)
		{
			delete[] mbcbuffer;
			mbcbuffer = NULL;
		}

		/*
		 arg1取值说明：
			START	开始合成	合成发音人，语速语调等
			CANCEL	取消合成	无
			PAUSE	暂停播放	无
			RESUME	恢复播放	无
		*/

		/*
		合成参数示例：
		String params = "vcn=xiaoyan,speed=50,pitch=50,volume=50"

		参数字段说明：
			vcn	发音人，如xiaoyan
			speed	速度，0-100
			pitch	语调，0-100
			volume	音量，0-100
			ent	引擎，默认aisound，如果需要较好的效果，可设置成xtts
		*/

		string paramStr = "vcn=xiaoyan";
		paramStr += ",speed=50";
		paramStr += ",pitch=50";
		paramStr += ",volume=50";

		cout << "params is " << paramStr << endl;

		IAIUIMessage * ttsMsg = IAIUIMessage::create(AIUIConstant::CMD_TTS,
			AIUIConstant::START, 0, paramStr.c_str(), textData);

		agent->sendMessage(ttsMsg);

		ttsMsg->destroy();
	}
}

void AIUITester::pauseTts()
{
	if (NULL != agent)
	{
		IAIUIMessage * ttsMsg = IAIUIMessage::create(AIUIConstant::CMD_TTS,
			AIUIConstant::PAUSE, 0, "", NULL);

		agent->sendMessage(ttsMsg);

		ttsMsg->destroy();
	}
}

void AIUITester::resumeTts()
{
	if (NULL != agent)
	{
		IAIUIMessage * ttsMsg = IAIUIMessage::create(AIUIConstant::CMD_TTS,
			AIUIConstant::RESUME, 0, "", NULL);

		agent->sendMessage(ttsMsg);

		ttsMsg->destroy();
	}
}

void AIUITester::cancelTts()
{
	if (NULL != agent)
	{
		IAIUIMessage * ttsMsg = IAIUIMessage::create(AIUIConstant::CMD_TTS,
			AIUIConstant::CANCEL, 0, "", NULL);

		agent->sendMessage(ttsMsg);

		ttsMsg->destroy();
	}
}

void AIUITester::destory()
{
	stopWriteThread();

	if (NULL != agent)
	{
		agent->destroy();
		agent = NULL;
	}
}